<?php
/**
 * 订单相关
 * xionggan
 * Date: 2019/5/30
 */

namespace service;

use app\account\model\CreditModel;
use app\common\model\CoreOrderServiceProduct;
use think\Db;
use think\Validate;
use think\Exception;
use other\BaseRedis;
use app\common\model\Product;
use app\common\model\CoreOrder;
use app\common\model\CoreOrderAction;
use app\common\model\CoreOrderPick;
use app\common\model\CoreOrderPickUser;
use app\common\model\CoreOrderProduct;
use app\common\model\CoreOrderRebate;
use app\common\model\CoreOrderDelivery;
use think\facade\Log;
use service\pay\VirtualPay;

class OrderService
{

    private $error          = null;
    protected $type         = null;
    protected $prefix       = null;
    protected $order_sn     = null;
    protected $order_model  = null;
    protected $condition    = null;
    protected $product      = [];
    protected $rebate       = [];

    public function __construct()
    {
        $this->order_model = new CoreOrder();
    }


    /**
     * 设置订单类型
     * @param   string  $type   订单类型
     * @return  null
     */
    public function setType($type)
    {
        $map = $this->order_model->getTypeMap();

        foreach ($map as $key => $value) {
            if ($value['prefix'] == $type) {
                $this->type     = $key;
                $this->prefix   = $value['prefix'];
                break;
            }
        }

        if (empty($this->type)) {
            throw new Exception('订单类型错误');
        }
    }

    /**
     * 获取订单号
     * @return  string  订单号
     */
    public function getOrderSn()
    {
        if (empty($this->type)) {
            throw new Exception('缺少提现类型');
        }

        $this->order_sn = $this->prefix.date('ymd').substr(
            implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8
        );

        try {
            $redis = new BaseRedis();

            if($redis->has("order_sn:".$this->order_sn)) {
                return $this->getOrderSn();
            }

            $redis->set("order_sn:".$this->order_sn, '', 60);
        } catch (Exception $e) {}

        return $this->order_sn;
    }

    /**
     * 添加订单商品数据
     * @param   array   $params     订单商品数据
     * @return  null
     */
    public function addProduct($params)
    {
        $rule = [
            'store_id'          => 'require',  
            'product_id'        => 'require',  
            'product_name'      => 'require',  
            'product_img'       => 'require',  
            'number'            => 'require',  
            'price'             => 'require',  
            'amount'            => 'require',  
            'amount_product'    => 'egt:0',
            'data_id'           => 'number',
            'price_origin'      => 'number',
            'balance'           => 'number',  
            'gold_coin'         => 'number',
            'amount_pay'        => 'number',
            'member_dis'        => 'number',
            'coupon_dis'        => 'number',
            'shipping_type'     => 'number',
            'close_time'        => 'number',
            'exdelivery_time'   => 'number',
            'postage'           => 'number',
        ];

        foreach ($rule as $key => $value) {
            $msg[$key.'.'.$value] = $value == 'require' ? '缺少参数:'.$key : '格式错误:'.$key;
        }

        $validate = new Validate($rule, $msg);
        $result   = $validate->check($params);
        
        if (!$result) {
            throw new Exception($validate->getError());
        }

        $params['create_time'] = time();
        $this->product[] = $params;
    }

    /**
     * 添加订单商品返利数据
     * @param   array   $params     订单商品返利数据
     * @return  null
     */
    public function addProductRebate($params)
    {
        $rule = [
            'product_id'        => 'require',  
            'number'            => 'require',  
            'price'             => 'require',  
            'amount'            => 'require',  
            'rebate_balance'    => 'require',  
            'rebate_coin'       => 'require',  
            'user_id'           => 'require',  
            'level'             => 'require',
        ];

        foreach ($rule as $key => $value) {
            $msg[$key.'.'.$value] = $value == 'require' ? '缺少参数:'.$key : '格式错误:'.$key;
        }

        $validate = new Validate($rule, $msg);
        $result   = $validate->check($params);
        
        if (!$result) {
            throw new Exception($validate->getError());
        }

        $this->rebate[] = $params;
    }

    /**
     * 创建订单
     * @param   array   $params     订单数据
     * @return  string  订单号
     */
    public function create($params)
    {
        $rule = [
            'store_id'          => 'require', 
            'user_id'           => 'require', 
            'balance'           => 'require',  
            'gold_coin'         => 'require',  
            'postage'           => 'require',  
            'amount'            => 'require',
            'amount_product'    => 'egt:0',
            'amount_dis'        => 'require',  
            'amount_pay'        => 'require',  
            'member_dis'        => 'egt:0',  
            'coin_dis'          => 'egt:0',  
            'coupon_dis'        => 'egt:0',  
            'coupon_ids'        => 'max:100',
            'self'              => 'require',  
            'self_point'        => 'number',  
            'status_pro'        => 'number',  
            'status_str'        => 'max:100',  
            'display'           => 'number', 
            'exdelivery_time'   => 'number',
            'parent_order'      => 'alphaNum',
            'self_name'         => 'max:100',
            'self_phone'        => 'max:15',
            'use_balance'       => 'number',
        ];

        foreach ($rule as $key => $value) {
            $msg[$key.'.'.$value] = $value == 'require' ? '缺少参数:'.$key : '格式错误:'.$key;
        }

        $validate = new Validate($rule, $msg);
        $result   = $validate->check($params);
        
        if (!$result) {
            throw new Exception($validate->getError());
        }

        if (empty($this->type)) {
            throw new Exception('缺少订单类型');
        }

        if (empty($this->product)) {
            throw new Exception('缺少订单商品');
        }

        if (empty($this->order_sn)) {
            $this->getOrderSn();
        }

        $params['order_sn']     = $this->order_sn;
        $params['type']         = $this->type;
        $params['create_time']  = time();

        $order_id = $this->order_model->addOne(
            $params, $this->product, $this->rebate
        );

        if (empty($order_id)) {
            throw new Exception($this->order_model->getError());
        }

        $order_sn = $this->order_sn;

        $this->order_sn = null;
        $this->product  = null;
        $this->rebate   = null;

        return $order_sn;
    }

    /**
     * 更新订单
     * @param   string  $order_sn   订单号
     * @param   array   $params     订单数据
     * @return  boole
     */
    public function update($order_sn, $params)
    {
        $rule = [
            'balance'           => 'egt:0',  
            'gold_coin'         => 'egt:0',  
            'postage'           => 'egt:0',  
            'amount'            => 'egt:0',  
            'amount_dis'        => 'egt:0',  
            'amount_pay'        => 'egt:0',  
            'member_dis'        => 'egt:0',  
            'coin_dis'          => 'egt:0',  
            'coupon_dis'        => 'egt:0',  
            'self'              => 'egt:0',  
            'self_point'        => 'number',  
            'status_pro'        => 'number',  
            'status_str'        => 'max:100',  
            'display'           => 'number', 
            'exdelivery_time'   => 'number',
        ];

        foreach ($rule as $key => $value) {
            $msg[$key.'.'.$value] = $value == 'require' ? '缺少参数:'.$key : '格式错误:'.$key;
        }

        $validate = new Validate($rule, $msg);
        $result   = $validate->check($params);

        if (!$result) {
            throw new Exception($validate->getError());
        }
        
        $this->order_model->editOne($order_sn, $params);
        return true;  
    }

    /**
     * 更新订单商品
     * @param   string  $order_pid  主键
     * @param   array   $params     商品数据
     * @return  boole
     */
    public function updateProduct($order_pid, $params)
    {
        $rule = [
            'balance'   => 'egt:0',  
            'gold_coin' => 'egt:0',  
        ];

        foreach ($rule as $key => $value) {
            $msg[$key.'.'.$value] = $value == 'require' ? '缺少参数:'.$key : '格式错误:'.$key;
        }

        $validate = new Validate($rule, $msg);
        $result   = $validate->check($params);
        
        if (!$result) {
            throw new Exception($validate->getError());
        }
        
        $product_model = new CoreOrderProduct();
        $product_model->editOne($order_pid, $params);
        return true;  
    }
    
    /**
     * 订单支付 (检查能否支付)
     * @param   string   $order_sn  订单编号
     * @return  boole
     */
    public function pay($order_sn)
    {
        $order_data = $this->getOneByOrderSn($order_sn);

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        $type_map = $this->getTypeMap();

        Db::startTrans();

        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderPay')) {
                throw new Exception('订单服务类方法不存在: orderPay');
            }

            $res = call_user_func_array([$service_order, 'orderPay'], [$order_sn]);

            Db::commit();

            if (!$res) {
                throw new Exception('订单已失效');
            }
            
            return $res;

        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 订单支付 (支付完成后的操作)
     * @param   string   $order_sn  订单编号
     * @return  boole
     */
    public function payed($order_sn)
    {
        $order_data = $this->getOneByOrderSn($order_sn);

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        $type_map = $this->getTypeMap();

        Db::startTrans();

        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderPayed')) {
                throw new Exception('订单服务类方法不存在: orderPayed');
            }

            $res = call_user_func_array([$service_order, 'orderPayed'], [$order_sn]);

            if (!$res) {
                Db::rollback();
                throw new Exception('订单已失效');
            }

        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }

        Db::commit();
        return true;
    }

    /**
     * 订单出库
     * @param   string   $order_sn      订单编号
     * @param   string   $delivery_sn   出库编号
     * @return  boole
     */
    public function check($order_sn, $delivery_sn='')
    {
        $order_data = $this->getOneByOrderSn($order_sn);

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        $core_model = new CoreOrder();
        $pro_model  = new CoreOrderProduct();
        $pick_model = new CoreOrderPick();
        $deli_model = new CoreOrderDelivery();
        $type_map   = $this->getTypeMap();

        Db::startTrans();

        try {

            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderShipping')) {
                throw new Exception('订单服务类方法不存在: orderShipping');
            }

            //更新发货单为已核销
            if (!empty($delivery_sn)) {
                $delivery_data = $deli_model->updateStatusCheck($delivery_sn);

                if (!empty($deli_model->getError())) {
                    throw new Exception($deli_model->getError());
                }

                if ($delivery_data['order_id'] != $order_data['order_id']) {
                    throw new Exception('出库编号与订单编号不匹配');
                }

                foreach ($delivery_data['product'] as $value) {
                    $pro_model->updateVerifyStatusAll($value['order_pid']);
                }

                $verify_all = $pro_model->checkVerifyStatusAll($order_data['order_id']);
                
                if ($verify_all) {
                    $core_model->updateVerifyStatusAll($order_sn);
                    $pick_model->updateStatusPickedByOrderId($order_data['order_id']);
                } else {
                    $core_model->updateVerifyStatusDepart($order_sn);
                }
            } else {
                //设置订单提货码为已提货
                $pick_model->updateStatusPickedByOrderId($order_data['order_id']);
                $this->setOrderShipped($order_sn); //更新订单状态
                $core_model->updateVerifyStatusAll($order_sn);
            }

            $res = call_user_func_array([$service_order, 'orderShipping'], [$order_sn]);
            $this->flushUserPickCode($order_data); //更新用户提货码
            
            if (!$res) {
                Db::rollback();
                throw new Exception('出库失败');
            }

        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }

        Db::commit();
        return true;
    }

    /**
     * 订单确认收货
     * @param   string   $order_sn  订单编号
     * @return  boole
     */
    public function received($order_sn)
    {
        $order_data = $this->getOneByOrderSn($order_sn);
        $type_map   = $this->getTypeMap();

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        Db::startTrans();
        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderShippingConfirm')) {
                throw new Exception('订单服务类方法不存在: orderShippingConfirm');
            }

            $this->setOrderReceived($order_sn);
            $this->setOrderFinished($order_sn);
            $res = call_user_func_array([$service_order, 'orderShippingConfirm'], [$order_sn]);

            if (!$res) {
                Db::rollback();
                throw new Exception('确认收货失败');
            }
        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }

        Db::commit();
        return true;
    }


    /**
     * 商品确认收货(新出库流程的商品订单)
     *
     * @param $order_sn
     * @param $product_ids
     * @return bool
     *
     * @throws Exception
     */
    public function received2($order_sn, $product_ids)
    {
        $order_data = $this->getOneByOrderSn($order_sn);
        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        $order_data = $order_data->toArray();
        /*检查商品是否在订单中*/
        if (empty($order_data['product'])) {
            throw new Exception('订单商品数据错误');
        }

        $order_product_ids = array_column($order_data['product'], 'product_id');

        foreach ($product_ids as $product_id) {
            if (!in_array($product_id, $order_product_ids)) {
                throw new Exception('订单商品错误');
            }
        }

        Db::startTrans();
        try {
            /*获取待收货的商品IDs（排除掉已收货等等非收货状态的id）*/
            $coreOrderDeliveryM = new CoreOrderDelivery();
            $product_ids = $coreOrderDeliveryM->getCanReceivedProductIds($order_data['order_id'], $product_ids);

            /*检查是否合法*/
            if (!$coreOrderDeliveryM->canReceive2($order_data['order_id'], $product_ids)) {
                throw new Exception('不能收货');
            }

            /*处理商品收货*/
            $orderDeliveryM = new CoreOrderDelivery();
            $order_id = $order_data['order_id'];
            if (!$orderDeliveryM->receive2($order_id, $product_ids)) {
                throw new Exception($orderDeliveryM->getError());
            }

            /*返积分*/
            $this->credit($order_data, $product_ids);
            /*增加收益*/
            $orderDeliveryM->deliveryIncome($order_sn, $product_ids);
            //返利
            $this->rebate($order_data, $product_ids);
            Db::commit();
            return true;
        } catch (Exception $e) {
            Db::rollback();
            $this->error = $e->getMessage();
            return false;
        }
    }

    public function getError()
    {
        return $this->error;
    }

    /**
     * 返积分
     *
     * @param $order_data
     * @param $product_ids
     * @throws Exception
     */
    private function credit($order_data, $product_ids)
    {
        $creditM = new CreditModel();
        foreach ($order_data['product'] as $product) {
            if (!in_array($product['product_id'], $product_ids)) {
                continue;
            }
            $sum = bcmul($product['amount'] - $product['member_dis'] - $product['gold_coin'] - $product['coupon_dis'], 100, 2);
            if ($sum <= 0) {
                continue;
            }
            $res_id = $creditM->shopping($order_data['user_id'], $sum, 0);
            if ($res_id <= 0) {
                throw new Exception('返还积分失败!');
            }
        }
    }

    /**
     * 返利
     *
     * @param $order_data
     * @param $product_ids
     */
    private function rebate($order_data, $product_ids)
    {
        $virtual_pay    = new VirtualPay();
        $rebate_model   = new CoreOrderRebate();
        $rebate_data    = $rebate_model->getAllNotByOrderId($order_data['order_id']);
        if (!empty($rebate_data)) {
            foreach ($rebate_data as $value) {
                if (!in_array($value['product_id'], $product_ids)) {
                    continue;
                }

                $rebate_balance = bcdiv($value['rebate_balance'], 100, 2);
                $rebate_coin    = bcdiv($value['rebate_coin'], 100, 2);

                $res = $virtual_pay->public_update_user_lockvirtual(
                    $value['user_id'], 0, 2, 3, $rebate_balance,
                    $rebate_coin, 0, $value['rebate_id'], $value['remark'], 1
                );

                $res > 0 && $rebate_model->updateRebateFinish($value['rebate_id']);
            }
        }
    }

    /**
     * 订单取消
     * @param   string   $order_sn  订单编号
     * @param   number   $reason    取消原因
     * @return  boole
     */
    public function cancel($order_sn, $reason=0)
    {
        $product_model  = new Product();
        $order_data     = $this->getOneByOrderSn($order_sn);
        $type_map       = $this->getTypeMap();

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        Db::startTrans();

        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderCancel')) {
                throw new Exception('订单服务类方法不存在: orderCancel');
            }
            
            $res = call_user_func_array([$service_order, 'orderCancel'], [$order_sn]);
            $this->setOrderCancelled($order_sn, $reason);

            if (!$res) {
                Db::rollback();
                throw new Exception('取消失败');
            }

            foreach ($order_data['product'] as $value) {
                $product_model->stockUpdate($value['product_id'], $value['number'], 2);
            }

        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }

        Db::commit();
        return true;
    }

    /**
     * 订单申请退款
     * @param   string   $order_sn  订单编号
     * @return  boole
     */
    public function refund($order_sn)
    {
        Log::info('SELF_ORDER: order_sn:' . $order_sn);
        $order_data = $this->getOneByOrderSn($order_sn);
        $type_map   = $this->getTypeMap();

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        Db::startTrans();

        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderRefund')) {
                throw new Exception('订单服务类方法不存在: orderRefund');
            }
            
            Log::info('SELF_ORDER:  call orderRefund');
            $res = call_user_func_array([$service_order, 'orderRefund'], [$order_sn]);
            Log::info('SELF_ORDER:  out orderRefund');
            $this->setOrderService($order_sn);
            if (!$res) {
                Log::info('SELF_ORDER:  rollback false');
                Db::rollback();
                throw new Exception('申请退款失败');
            }

        } catch (Exception $e) {
            Db::rollback();
            Log::info('SELF_ORDER:  catch error :' . $e->getMessage());
            throw new Exception($e->getMessage());
        }

        Db::commit();
        return true;
    }

    /**
     * 订单取消申请退款
     * @param   string   $order_sn  订单编号
     * @return  boole
     */
    public function refundCancel($order_sn)
    {
        $order_data = $this->getOneByOrderSn($order_sn);
        $type_map   = $this->getTypeMap();

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        Db::startTrans();

        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderRefundCancel')) {
                throw new Exception('订单服务类方法不存在: orderRefundCancel');
            }

            $this->setOrderPaid($order_sn);
            $res = call_user_func_array([$service_order, 'orderRefundCancel'], [$order_sn]);

            if (!$res) {
                Db::rollback();
                throw new Exception('取消申请退款失败');
            }

        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }

        Db::commit();
        return true;
    }

    /**
     * 订单评价
     * @param   string  $order_sn   订单编号
     * @param   string  $content    评价内容
     * @param   array   $images     评价图片
     * @param   array   $parmas     其他数据
     * @return  boole
     */
    public function comment($order_sn, $content, $images, $parmas=[])
    {
        $order_data = $this->getOneByOrderSn($order_sn);
        $type_map   = $this->getTypeMap();

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        Db::startTrans();

        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderComment')) {
                throw new Exception('订单服务类方法不存在: orderComment');
            }

            $this->setOrderCommented($order_sn);
            $res = call_user_func_array([$service_order, 'orderComment'], [$order_sn, $content, $images, $parmas]);

            if (!$res) {
                Db::rollback();
                throw new Exception('评价失败');
            }

        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }

        Db::commit();
        return true;
    }

    /**
     * 订单详情
     * @param   string   $order_sn  订单编号
     * @return  array               订单详情
     */
    public function detail($order_sn)
    {
        $order_data = $this->getOneByOrderSn($order_sn);
        $type_map   = $this->getTypeMap();

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }
        
        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderDetail')) {
                throw new Exception('订单服务类方法不存在: orderDetail');
            }

            $res = call_user_func_array([$service_order, 'orderDetail'], [$order_sn]);

            if (!$res) {
                throw new Exception('查看详情失败');
            }
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }

        return $res;
    }

    /**
     * 订单删除
     * @param   string   $order_sn  订单编号
     * @return  boole
     */
    public function delete($order_sn)
    {
        $order_data = $this->getOneByOrderSn($order_sn);

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        $type_map = $this->getTypeMap();

        Db::startTrans();

        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderDelete')) {
                throw new Exception('订单服务类方法不存在: orderDelete');
            }

            $res = call_user_func_array([$service_order, 'orderDelete'], [$order_sn]);

            if (!$res) {
                Db::rollback();
                throw new Exception('订单删除失败');
            }

        } catch (Exception $e) {
            Db::rollback();
            throw new Exception($e->getMessage());
        }

        Db::commit();
        return true;
    }

    /**
     * 关闭（恢复）订单动作
     * @param   string  $order_sn   订单编号
     * @param   string  $action     动作类型
     * @param   string  $do         操作类型
     * @return  boole
     */
    public  function closeActions($order_sn, $action, $do='off')
    {

    }

    /**
     * 售后订单详情
     * @param   string   $order_sn  订单编号
     * @return  array               订单详情
     */
    public function serviceDetail($order_sn)
    {
        $order_data = $this->getOneByOrderSn($order_sn);
        $type_map   = $this->getTypeMap();

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }
        
        try {
            $prefix = $type_map[$order_data['type']]['prefix'];
            $service_name = 'service\order\\OrderService'.strtoupper($prefix);

            if (!class_exists($service_name)) {
                throw new Exception('订单服务类不存在: '.$service_name);
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderServiceDetail')) {
                throw new Exception('订单服务类方法不存在: orderServiceDetail');
            }

            $res = call_user_func_array([$service_order, 'orderServiceDetail'], [$order_sn]);

            if (!$res) {
                throw new Exception('查看售后详情失败');
            }
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }

        return $res;
    }

    /**
     * 订单超时检测
     * @return  null
     */
    public function overdue($user_id)
    {
        $type_map = $this->getTypeMap();

        if (empty($type_map)) {
            return;
        }

        foreach ($type_map as $value) {
            $service_name   = 'service\order\\OrderService'.strtoupper($value['prefix']);

            if (!class_exists($service_name)) {
                continue;
            }

            $service_order = new $service_name;

            if (!method_exists($service_order, 'orderOverdue')) {
                continue;
            }

            try {
                call_user_func_array([$service_order, 'orderOverdue'], [$user_id]);
            } catch (Exception $e) {

            }
            
            $service_order  = null;
            $service_name   = null;
        }
    }

    /**
     * 设置订单支持的操作
     * @param   string  $order_sn   订单号
     * @param   array   $actions    操作列表
     * @return  boole
     */
    public function setAction($order_sn, array $actions)
    {
        $order_data = $this->order_model->getOneBySn($order_sn);

        if (empty($order_data)) {
            return;
            throw new Exception('订单不存在');
        }

        $action_model = new CoreOrderAction();
        $res = $action_model->editOne($order_data['order_id'], $actions);

        if (!$res) {
            throw new Exception($action_model->getError());
        }

        return true;
    }

    /**
     * 添加提货码(废除)
     * @param   array  $params   参数
     * @return  boole
     */
    public function addPickCode($params)
    {
        return;

        $format = [
            'order_sn', 'store_id', 'pick_point', 'pick_code', 'pick_qr', 'pick_order'
        ];

        foreach ($format as $value) {
            if (!isset($params[$value])) {
                throw new Exception('添加提货码失败,缺少 '.$value);
            }
        }

        $order_info = $this->order_model->getOneBySn($params['order_sn']);

        if (empty($order_info)) {
            throw new Exception('订单不存在');
        }

        $params['order_id']     = $order_info['order_id'];
        $params['create_time']  = time();

        try {
            $pick_model = new CoreOrderPick();
            $pick_model->addOne($params);
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }

        return true;
    }

    /**
     * 生成提货码
     * @param   array   $params     参数
     * @return  array   提货码数据
     */
    public function genPickCode($params)
    {
        $format = [
            'order_sn', 'store_id', 'pick_point'
        ];

        foreach ($format as $value) {
            if (!isset($params[$value])) {
                throw new Exception('生成提货码失败,缺少 '.$value);
            }
        }

        $order_data = $this->order_model->getOneBySn($params['order_sn']);

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        $pick_model = new CoreOrderPick();

        // $pick_data  = $pick_model->getOneForUnpick($order_data['user_id']);

        // if (count($pick_data) > 0) {
        //     $code_data = [
        //         'pick_code' => $pick_data[0]['pick_code'],
        //         'path'      => $pick_data[0]['pick_qr'],
        //     ];
        // } else {
        //     $code_data = create_pick_code();
        // }

        $code_data = create_pick_code();

        if (!$code_data) {
            throw new Exception('提供码生成失败');
        }

        $data = $pick_model->genPickOrder([
            'order_id'      => $order_data['order_id'], 
            'store_id'      => $params['store_id'],
            'user_id'       => $order_data['user_id'],
            'pick_point'    => $params['pick_point'], 
            'pick_code'     => $code_data['pick_code'], 
            'pick_qr'       => $code_data['path'], 
            'pick_order'    => '',
        ]);
        
        if (empty($data)) {
            throw new Exception($pick_model->getError());
        }

        return $data;
    }

    /**
     * 更新用户提货码
     * @param   array   $params    提货码 ['user_id' => 1]
     * @return  array   提货码数据
     */
    public function flushUserPickCode($params)
    {   
        $pick_model = new CoreOrderPickUser();
        $code_data  = create_pick_code();

        if (!$code_data) {
            throw new Exception('提供码生成失败');
        }

        $pick_code = [
            'user_id'       => $params['user_id'],
            'pick_code'     => $code_data['pick_code'], 
            'pick_qr'       => $code_data['path'], 
        ];

        $pid = $pick_model->addOne($pick_code);
        
        if (empty($pid)) {
            throw new Exception($pick_model->getError());
        }

        return $pick_code;
    }

    /**
     * 获取用户提货码
     * @param   array   $params    提货码 ['user_id' => 1]
     * @return  array   提货码数据
     */
    public function getUserPickCode($params)
    {
        $pick_model = new CoreOrderPickUser();
        $data = $pick_model->getOneByUserId($params['user_id']);

        if (empty($data)) {
            $data = $this->flushUserPickCode($params);
            $data['pick_qr_r'] = get_img_url($data['pick_qr'], 300);
        }

        return $data;
    }

    /**
     * 获取当前订单所处的
     * 订单主状态和发货状态
     *
     * @param $order_sn
     * @return array $return
     *
     * [
     *    'status' => 'core_order 里status对应的值',
     *    'delivery_status' => 'core_order 里delivery_status对应的值'
     * ]
     *
     * @throws Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function checkOrderStatus($order_sn)
    {
        /*获取订单及其订单商品信息*/
        $coreM      = new CoreOrder();
        $order_data = $coreM
            ->with(['product' => ['service_product']])
            ->where(['order_sn' => $order_sn])
            ->find();

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }
        $order_data             = $order_data->toArray();

        $old_status             = $order_data['status'];
        $old_delivery_status    = $order_data['delivery_status'];

        $return = [
            'status'            => $old_status, /*订单主状态*/
            'delivery_status'   => $old_delivery_status /*订单发货状态*/
        ];

        /*不参与判断的主状态*/
        $extra_status = [
            CoreOrder::STATUS_0,
            CoreOrder::STATUS_6,
            CoreOrder::STATUS_7,
            CoreOrder::STATUS_8,
            CoreOrder::STATUS_9,
            CoreOrder::STATUS_10,
            CoreOrder::STATUS_11
        ];

        /** self::STATUS_0  => '待支付',
        self::STATUS_1  => '待发货',
        self::STATUS_2  => '待收货',
        self::STATUS_3  => '已收货',
        self::STATUS_4  => '申请退款',
        self::STATUS_5  => '售后',
        self::STATUS_6  => '完成',
        self::STATUS_7  => '已退款',
        self::STATUS_8  => '拒绝退款',
        self::STATUS_9  => '取消',
        self::STATUS_10 => '待处理',
        self::STATUS_11 => '作废',
         * */

        /*排除确定的主状态*/
        if (in_array($old_status, $extra_status)) {
            return $return;
        }

        /*先检查商品售后，排除掉售后已经完成的商品*/
        if (empty($order_data['product'])) {
            throw new Exception('订单商品不存在');
        }

        $exclude_product_ids    = [];
        $exclude_service_status = [
            CoreOrderServiceProduct::STATUS_4,
            CoreOrderServiceProduct::STATUS_1,
            CoreOrderServiceProduct::STATUS_2,
            CoreOrderServiceProduct::STATUS_3
        ];
        foreach ($order_data['product'] as $key => $v) {
            if (empty($v['service_product'])) {
                continue;
            }
            if (in_array($v['service_product']['status'], $exclude_service_status)) {
                $exclude_product_ids[] = $v['product_id'];
            }
        }

        /*先根据商品的delivery_status检查订单的主状态*/
        $delivery_map = [
            CoreOrderProduct::DELIVERY_STATUS_0 => 0,
            CoreOrderProduct::DELIVERY_STATUS_2 => 0,
            CoreOrderProduct::DELIVERY_STATUS_3 => 0,
        ];
        $product_num            = count($order_data['product']);
        $include_product_num    = $product_num - count($exclude_product_ids); /*参与判断发货状态的商品数*/
        foreach ($order_data['product'] as $p) {
            if (in_array($p['product_id'], $exclude_product_ids)) {
                continue;
            }
            $delivery_map[$p['delivery_status']] += 1;
        }

        /**
        self::DELIVERY_STATUS_0  => '待发货',
        self::DELIVERY_STATUS_1  => '部分发货',
        self::DELIVERY_STATUS_2  => '已发货',
        self::DELIVERY_STATUS_3  => '已收货',
         * */
        $cur_status             = $old_status;
        $cur_delivery_status    = $old_delivery_status;
        if ($delivery_map[CoreOrderProduct::DELIVERY_STATUS_0] > 0) {
            $cur_status             = CoreOrder::STATUS_1;
            $cur_delivery_status    = CoreOrder::DELIVERY_STATUS_1;
            if ($delivery_map[CoreOrderProduct::DELIVERY_STATUS_0 == $include_product_num]) {
                $cur_delivery_status = CoreOrderProduct::DELIVERY_STATUS_0;
            }
        } elseif ($delivery_map[CoreOrderProduct::DELIVERY_STATUS_2] > 0) {
            $cur_status             = CoreOrder::STATUS_2;
            $cur_delivery_status    = CoreOrder::DELIVERY_STATUS_2;
        } elseif ($delivery_map[CoreOrderProduct::DELIVERY_STATUS_3] > 0) {
            $cur_status             = CoreOrder::STATUS_6;
            $cur_delivery_status    = CoreOrder::DELIVERY_STATUS_3;
        }

        /*检查订单商品中处于售后的商品的状态(逻辑备忘，全部售后，则主状态为售后，存在售后，则主状态为当前主状态)*/
        $service_status_num = 0;
        foreach ($order_data['product'] as $pro) {
            if ($pro['service_status'] == CoreOrderProduct::SERVICE_STATUS_1) {
                $service_status_num += 1;
            }
        }
        if ($service_status_num == $product_num) {
            $cur_status = CoreOrder::STATUS_5;
        }

        $return['status']           = $cur_status;
        $return['delivery_status']  = $cur_delivery_status;
        return $return;
    }

    /**
     * 设置提货码为已扫码状态
     * @param   array  $params     参数
     * @return  boole
     */
    public function setPickCodeScanned($params)
    {
        $format = ['pick_code'];

        foreach ($format as $value) {
            if (empty($params[$value])) {
                throw new Exception('设置提货码状态失败,缺少 '.$value);
            }
        }

        $pick_model = new CoreOrderPick();
        $pick_model->updateStatusScanned($params['pick_code']);
    }

    /**
     * 设置提货码为已提货状态
     * @param   array  $params     参数
     * @return  boole
     */
    public function setPickCodePicked($params)
    {
        $format = ['pick_code'];

        foreach ($format as $value) {
            if (empty($params[$value])) {
                throw new Exception('设置提货码状态失败,缺少 '.$value);
            }
        }

        $pick_model = new CoreOrderPick();
        $pick_model->updateStatusPicked($params['pick_code']);
    }

    /**
     * todo 增加待评价条件项搜索
     * 获取订单列表
     * @param   array   $params 查询参数: ['condition' => [], 'sort' => ['field' => '', 'dire' => ''], 'page' => 1, 'limit' => 10]
     * @param   boole   $stats  是否按状态统计订单数
     * @return  array           订单数据
     */
    public function getList($params, $stats=false)
    {
        return $this->order_model->getList($params, $stats);
    }

    /**
     * todo 增加待评价条件项搜索
     * 获取订单列表状态统计
     * @param   array   $params 查询参数: ['condition' => ['user_id' => 1]]
     * @return  array           订单数据
     */
    public function getListStats($params)
    {
        return $this->order_model->getListStats($params);
    }

    /**
     * 商家端获取订单数
     * @param   number  $store_id       商家id
     * @param   boole   $order_type     订单类型
     * @return  number                  订单数
     */
    public function getOrderCountForStore($store_id, $order_type=[])
    {
        return $this->order_model->getOrderCountByStoreid($store_id, $order_type);
    }

    /**
     * 根据提货码查找订单
     * @param   string  $code   提货码
     * @return  array           订单信息
     */
    public function getOrderByPickCode($code)
    {
        $pick_model = new CoreOrderPickUser();
        $pick_info  = $pick_model->getOneByPickCode($code);

        if (empty($pick_info)) {
            throw new Exception('提货码不存在');
        }

        $order_data = $this->order_model->getOneByUserPk($pick_info['user_id'],['status'=>[1,2]]);

        if (empty($order_data)) {
            throw new Exception('订单不存在');
        }

        $order_check = $this->order_model->getOneForCheckOut($order_data['store_id'], $order_data['order_sn']);

        if (empty($order_check)) {
            throw new Exception($this->order_model->getError());
        }

        $order_data['pick_info'] = $pick_info;

        return $order_data;
    }

    /**
     * 商家端获取订单
     * @param   string   $store_id  商家id
     * @param   string   $order_sn  订单编号
     * @return  array               订单数据
     */
    public function getOneForStore($store_id, $order_sn)
    {
        return $this->order_model->getOneForStore($store_id, $order_sn);
    }

    /**
     * 商家端获取订单出库
     * @param   string   $store_id  商家id
     * @param   string   $order_sn  订单编号
     * @return  array               订单数据
     */
    public function getOneForCheckOut($store_id, $order_sn)
    {
        $data = $this->order_model->getOneForCheckOut($store_id, $order_sn);
        if (empty($data)) {
             throw new Exception($this->order_model->getError());
        }
        return $data;
    }
    
    /**
     * 获取订单
     * @param   string   $order_sn  订单编号
     * @return  array               订单数据
     */
    public function getOneByOrderSn($order_sn)
    {
        return $this->order_model->getOneBySn($order_sn);
    }

    /**
     * 获取出库记录和出库商品
     * @param string $delivery_sn 订单出库编号
     * @return array
     */
    public function getDelivery($delivery_sn)
    {
        return (new CoreOrderDelivery())->getDeliveryBySn($delivery_sn);
    }

    /**
     * 订单编号获取一个待收货的自提订单订单
     * @param   string   $order_sn  订单编号
     * @return  array               订单数据
     */
    public function getOneBySnBySelf($order_sn)
    {
        return $this->order_model->getOneBySnBySelf($order_sn);
    }

    /**
     * 获取订单状态表
     * @return  array 状态列表
     */
    public function getStatusMap()
    {
        return $this->order_model->getStatusMap();
    }

    /**
     * 获取订单类型表
     * @return  array 类型列表
     */
    public function getTypeMap()
    {
        return $this->order_model->getTypeMap();
    }

    /**
     * 设置订单为已支付
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderPaid($order_sn)
    {
        try {
            $this->order_model->updateOrderPaid($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为已发货
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderShipped($order_sn)
    {
        try {
            $this->order_model->updateOrderShipped($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为已收货
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderReceived($order_sn)
    {
        try {
            $this->order_model->updateOrderReceived($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为申请退款 (暂不使用)
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderRefund($order_sn)
    {
        throw new Exception('暂停使用，请使用 setOrderRefunded');
        try {
            $this->order_model->updateOrderRefund($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为已退款
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderRefunded($order_sn)
    {
        try {
            $this->order_model->updateOrderRefunded($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为已拒绝退款 (暂不使用)
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderRefundedReject($order_sn)
    {
        throw new Exception('暂停使用');
        try {
            $this->order_model->updateOrderRefundedReject($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为售后
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderService($order_sn)
    {
        try {
            $this->order_model->updateOrderService($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为已完成
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderFinished($order_sn)
    {
        try {
            $this->order_model->updateOrderFinished($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为已评价
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderCommented($order_sn)
    {
        try {
            $this->order_model->updateOrderCommented($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为未评价
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderNoCommented($order_sn)
    {
        try {
            $this->order_model->updateOrderNoCommented($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为已取消
     * @param   string  $order_sn   订单编号
     * @param   number  $reason     取消原因
     * @return  boole
     */
    public function setOrderCancelled($order_sn, $reason=0)
    {
        try {
            $this->order_model->updateOrderCancelled($order_sn, $reason);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为已出库 (自提订单后台出库操作)
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderOutbound($order_sn)
    {
        try {
            $this->order_model->updateOrderOutbound($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 设置订单为待处理
     * @param   string  $order_sn   订单编号
     * @return  boole
     */
    public function setOrderPending($order_sn)
    {
        try {
            $this->order_model->updateOrderPending($order_sn);
            return true;
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
    }


}
